/**
 * PHOSIDE: PHosphorylation Site IDentification Engine.
 * Copyright 2009 Digital Biology Lab, University of Missouri.
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version. <p/> This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more
 * details.
 */

package phoside;

import cytoscape.task.TaskMonitor;

import java.io.IOException;

import java.util.List;
import java.util.Properties;
import java.util.Set;

import phoside.PhosphoProteins.PhosphoType;

import phoside.classify.BinaryClassifier;
import phoside.classify.result.PhosidePredictionResult;
import phoside.classify.result.PhosidePredictionResultImpl;
import phoside.classify.result.ResultInterpreter;

import phoside.data.feature.extaction.InstancesExtractor;
import phoside.data.feature.extaction.InstancesExtractorFromProteins;
import phoside.data.feature.model.Instance;
import phoside.data.feature.model.InstanceTag;

import phoside.model.PhosideModel;

import phoside.util.StaticFinalValues;


/**
 *
 * @author gjj
 */
public class PhosideClassify {
    
    private TaskMonitor monitor;
    private boolean selectivePrediction = false;
    private int jobSize = -1;
//    private boolean keepSeqInRes = true;
    
    public void setSelectivePrediction(boolean selectivePrediction) {
        this.selectivePrediction = selectivePrediction;
    }

//    public void setKeepSequenceInResult(boolean keepSeqInRes) {
//        this.keepSeqInRes = keepSeqInRes;
//    }

    public void setJobSize(int jobSize) {
        this.jobSize = jobSize;
    }

    public void setTaskMonitor(TaskMonitor monitor) {
        this.monitor = monitor;
    }

    private void setMonitorStatus(String status) {
        if (monitor!=null) {
            monitor.setStatus(status);
        }
    }

    private void setMonitorStatus(String status, int perc) {
        if (monitor!=null) {
            monitor.setStatus(status);
            monitor.setPercentCompleted(perc);
        }
    }

    public void interrupt() {

    }

    public PhosidePredictionResult classify(final PhosideModel model,
             Proteins proteins) throws IOException {
        return classify(model, proteins, true);
    }

    public PhosidePredictionResult classify(final PhosideModel model,
             Proteins proteins, boolean predictTerminal) throws IOException {
        Set<PhosphoType> types = model.getSiteTypes();

        Properties props = model.getModelProperties();

        int maxOffset = 0;

        // knn param
        boolean useKNN = props.getProperty(StaticFinalValues.USEKNNFEATURES).
                    equalsIgnoreCase("true");
        int knnWindowOffset = Integer.parseInt(props.getProperty(
                    StaticFinalValues.KNNWINDOWSIZE))/2;
        if (useKNN && knnWindowOffset>maxOffset) {
            maxOffset = knnWindowOffset;
        }

        // disorder param
        boolean useDisorder = props.getProperty(StaticFinalValues.
                    USEDISORDERFEATURES).equalsIgnoreCase("true");
        String strDisWindows = props.getProperty(
                    StaticFinalValues.DISORDERWINDOWSIZES);
        String[] strs = strDisWindows.split(",");
        int[] disWindowOffsets = new int[strs.length];
        for (int i=0; i<strs.length; i++) {
            disWindowOffsets[i] = Integer.parseInt(strs[i].trim())/2;
            if (useDisorder && disWindowOffsets[i]>maxOffset) {
                maxOffset = disWindowOffsets[i];
            }
        }

        // frequency param
        boolean useFrequency = props.getProperty(StaticFinalValues.
                    USEFREQUENCYFEATURES).equalsIgnoreCase("true");
        int frequencyWindow = Integer.parseInt(props.getProperty(
                    StaticFinalValues.FREQUENCYWINDOWSIZE))/2;
        if (useFrequency && frequencyWindow>maxOffset) {
            maxOffset = frequencyWindow;
        }

        ResultInterpreter ri = model.getROC()!=null?new ResultInterpreter(model.getROC()):null;
        PhosidePredictionResult classifyResult;
        classifyResult = new PhosidePredictionResultImpl(ri, proteins);
        
        int njob = (proteins.proteinCount()+jobSize-1)/jobSize;

        int insCount = 0;

        InstancesExtractor.PhosphoTag phosphoTag = selectivePrediction?
            InstancesExtractor.PhosphoTag.PHOSPHO:InstancesExtractor.PhosphoTag.BOTH;
        InstancesExtractorFromProteins insExtractor = new InstancesExtractorFromProteins(
                proteins, null, types, predictTerminal?-1:maxOffset, phosphoTag);
        for (int ijob=0; insExtractor.hasMore(); ijob++) {
            // extract instances
            System.out.println("Classifying...");
            int total = proteins.proteinCount();
            int start = ijob*jobSize+1;
            int end = (ijob+1)*jobSize;
            if (end>total)
                end = total;
            setMonitorStatus("Classifying: "+start+"-"+end+" of "+total+" proteins.", jobSize==-1?-1:(int)(ijob*100.0/njob));
            List<Instance> instances_unlabeled = insExtractor.fetch(jobSize);

            BinaryClassifier classifier = model.getClassifier();
            List<Double> prediction = classifier.classify(instances_unlabeled);

            int nins = prediction.size();
            insCount += nins;
            for (int i=0; i<nins; i++) {
                Instance ins = instances_unlabeled.get(i);
                InstanceTag tag = ins.getInstanceTag();
                Protein protein = (Protein)tag.getTag(StaticFinalValues.INSTANCETAGNAMEPROTEIN);
                String acc = protein.getAccession();
                int site = (Integer)tag.getTag(StaticFinalValues.INSTANCETAGNAMESITE);

                classifyResult.setPrediction(acc, site, prediction.get(i));
            }
        }

        System.out.println("Classified for "+insCount+" sites.");

        return classifyResult;
    }

}
